"""
Problem 19: https://projecteuler.net/problem=19

Counting Sundays

You are given the following information, but you may prefer to do some research
for yourself.

1 Jan 1900 was a Monday.
Thirty days has September, April, June and November.
All the rest have thirty-one,
Saving February alone, Which has twenty-eight, rain or shine.
And on leap years, twenty-nine.

A leap year occurs on any year evenly divisible by 4, but not on a century
unless it is divisible by 400.

How many Sundays fell on the first of the month during the twentieth century
(1 Jan 1901 to 31 Dec 2000)?
"""

# _*_ conding:UTF-8 _*_
'''
@author = Kuperain
@email  = kuperain@aliyun.com
@IDE    = Geany Python3.8.3
@date   = 2022/5/9
'''

WEEK = {
    1: 'Modday',
    2: 'Tuesday',
    3: 'Wednesaday',
    4: 'Thursday',
    5: 'Friday',
    6: 'Saturday',
    7: 'Sunday'}

    
def isLeapyear(year: int = 2022) -> int:
    '''
    A leap year occurs on any year evenly divisible by 4, but not on a century
    unless it is divisible by 400.
    
    >>> assert isLeapyear(2000) and not isLeapyear(2100)
    '''
    
    cond1 = year % 4 == 0 and year % 100 != 0
    cond2 = year % 400 == 0
    
    return cond1 or cond2
    

def monthdays(year: int, month: int) -> int:
    '''
    Thirty days has September, April, June and November.
    All the rest have thirty-one,
    Saving February alone, Which has twenty-eight, rain or shine.
    And on leap years, twenty-nine.
    
    >>> assert monthdays(2022,2) == 28
    '''
    if month in {4,6,9,11}:
        return 30
    if month in {1,3,5,7,8,10,12}:
        return 31
    if month == 2 and isLeapyear(year):
        return 29
    else:
        return 28

def whatday(year: int, month: int, day: int) -> list:
    '''
    
    1 Jan 1900 was a Monday.
    
    >>> whatday(1, 1, 1)
    7
    >>> whatday(1880, 1, 1)
    3
    >>> whatday(1900, 1, 1)
    1
    >>> whatday(2022, 5, 9)
    1
    '''

    year0  = 1900
    month0 = 1
    day0   = 1
    
    days = 0
    for y in range(year0, year, 1 if year >= year0 else -1):
        days += 366 if isLeapyear(y) else 365
    
    # days of current year
    current_year_days = 0
    for m in range(1, month):
        current_year_days += monthdays(year, m)
        
    current_year_days = current_year_days + day
    
    if year >= year0:
        days += current_year_days
    else:
        days += 366 - current_year_days if isLeapyear(y) else (
                365 - current_year_days)
    
    return 7 if days % 7 == 0 else days % 7


def solution(day1: tuple = (1901,1), day2: tuple = (2000,12)) -> int:
    
    if day1[0] > day2[0]:
        day2, day1 = day1, day2
    
    firstyear = [(day1[0], m, 1) for m in range(day1[1], 13)]
    lastyear  = [(day2[0], m, 1) for m in range(1,day2[1]+1)]
    midyear   = [(y, m, 1) for y in range(day1[0]+1, day2[0]) for m in range(1,13)]
    months   = firstyear + midyear + lastyear
    # print(months)
    
    firstDayisSunday = lambda x : whatday(*x) == 7
    
    return sum(map(firstDayisSunday, months))
        
    


if __name__ == "__main__":
    import doctest
    doctest.testmod(verbose = False)
    
    print(solution())
    # 171
    
    
    
    


    


